package io.github.kimmking.gateway.outbound.okhttp;

import io.github.kimmking.gateway.filter.HeaderHttpResponseFilter;
import io.github.kimmking.gateway.filter.HttpRequestFilter;
import io.github.kimmking.gateway.filter.my.MyHttpRespnseFilter;
import io.github.kimmking.gateway.filter.my.MyHttpResponseFilterChain;
import io.github.kimmking.gateway.outbound.httpclient4.NamedThreadFactory;
import io.github.kimmking.gateway.router.my.MyHttpEndpointRouterDelegate;
import io.github.kimmking.gateway.router.my.MyRoundRibbonHttpEndpointRouter;
import io.github.kimmking.gateway.router.my.MyWeightHttpEndpointRouter;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import job6.okHttp.OkHttpRetryInterceptor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

/**
 * 1. 整合了OkhttpClient
 * 2.整合filterChain
 */
@Slf4j
public class OkhttpOutboundHandler {

    private OkHttpClient okHttpClient;
    //原逻辑
    private ExecutorService proxyService;
    private List<String> backendUrls;

    //改为调用filterChain
    MyHttpResponseFilterChain responseFilterChain = new MyHttpResponseFilterChain(true);

    MyHttpEndpointRouterDelegate routerDelegate = new MyHttpEndpointRouterDelegate(
            //        new MyRoundRibbonHttpEndpointRouter()
            new MyWeightHttpEndpointRouter(Arrays.asList(new Integer[]{1, 3}))
    );

    public OkhttpOutboundHandler(List<String> backends) {
        //同HttpOutBoundHandler
        this.backendUrls = backends.stream().map(this::formatUrl).collect(Collectors.toList());

        int cores = Runtime.getRuntime().availableProcessors();
        long keepAliveTime = 5000;
        int queueSize = 5000;
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
        proxyService = new ThreadPoolExecutor(cores, cores,
                keepAliveTime, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(queueSize),
                new NamedThreadFactory("proxyService"), handler);

        //初始化okHttpClient
        try {
            //如果发送失败，会重试3次，如果重试也都失败了，则一共会发送4次
            OkHttpRetryInterceptor okHttpRetryInterceptor = new OkHttpRetryInterceptor(3, 5000);
            OkHttpClient.Builder retryableBuilder = new OkHttpClient.Builder()
                    .retryOnConnectionFailure(true)
                    .addInterceptor(okHttpRetryInterceptor)
                    .readTimeout(3, TimeUnit.SECONDS)
                    .connectTimeout(3, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS);
            retryableBuilder.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });
            okHttpClient = retryableBuilder.build();
        } catch (Exception e) {
            log.error("okHttpClient 创建失败", e);
        }

        //初始化httpResponseFilterChain
        responseFilterChain.addFilter(new HeaderHttpResponseFilter());
        responseFilterChain.addFilter(new MyHttpRespnseFilter());
    }

    /**
     * 去掉Url末尾的"/"
     *
     * @param backend
     * @return "/hello/" => "/hello"
     */
    private String formatUrl(String backend) {
        return backend.endsWith("/") ? backend.substring(0, backend.length() - 1) : backend;
    }

    public void handle(final FullHttpRequest fullRequest, final ChannelHandlerContext ctx, HttpRequestFilter filter) {
        String backendUrl = routerDelegate.route(this.backendUrls);
        final String url = backendUrl + fullRequest.uri();
        //跳过该url转发
        if (url.endsWith("favicon.ico")) {
            return;
        }
        filter.filter(fullRequest, ctx);
        proxyService.submit(() -> fetchGet(fullRequest, ctx, url));
    }

    /**
     * 使用okHttpClient请求url并
     *
     * @param inbound
     * @param ctx
     * @param url
     */
    private void fetchGet(final FullHttpRequest inbound, final ChannelHandlerContext ctx, final String url) {
        Request.Builder builder = new Request.Builder().addHeader("Connection", "keep-alive");
        Request request = builder.url(url).build();
        try {
            Response response = okHttpClient.newCall(request).execute();
            handleResponse(inbound, ctx, response);
        } catch (IOException e) {
            log.error("okHttpClient call IO error!", e);
        } catch (Exception e) {
            log.error("okHttpClient call error!", e);
        }

    }

    private void handleResponse(final FullHttpRequest fullRequest, final ChannelHandlerContext ctx, final Response okHttpResponse) throws Exception {
        FullHttpResponse response = null;
        try {
//            String value = "hello,kimmking";
//            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(value.getBytes("UTF-8")));
//            response.headers().set("Content-Type", "application/json");
//            response.headers().setInt("Content-Length", response.content().readableBytes());
            //  String responseStr = okHttpResponse.body().string();
            //   log.info("okHttpClient: {}", responseStr);

            byte[] body = okHttpResponse.body().bytes();
//            System.out.println(new String(body));
//            System.out.println(body.length);

            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(body));

            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", Integer.parseInt(okHttpResponse.headers().get("Content-Length")));

            responseFilterChain.filter(response);

//            for (Header e : endpointResponse.getAllHeaders()) {
//                //response.headers().set(e.getName(),e.getValue());
//                System.out.println(e.getName() + " => " + e.getValue());
//            }

        } catch (Exception e) {
            e.printStackTrace();
            response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);
            exceptionCaught(ctx, e);
        } finally {
            if (fullRequest != null) {
                if (!HttpUtil.isKeepAlive(fullRequest)) {
                    ctx.write(response).addListener(ChannelFutureListener.CLOSE);
                } else {
                    //response.headers().set(CONNECTION, KEEP_ALIVE);
                    ctx.write(response);
                }
            }
            ctx.flush();
            //ctx.close();
        }

    }

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }


}
